import { Point, RouterDirectionType, RouterDirection } from "./types";

export class ManhattanRouter {
  public TOL: number = 1;
  public TOGGLE_DIST: number = 5;
  public MINDIST: number = 30;

  public route(
    conn: Point[],
    fromPt: Point,
    fromDir: RouterDirectionType,
    toPt: Point,
    toDir: RouterDirectionType,
  ): void {
    const TOL: number = this.TOL;
    const TOLxTOL: number = TOL * TOL;
    const MINDIST: number = this.MINDIST;
    const LEFT: RouterDirectionType = RouterDirection.Left;
    const RIGHT: RouterDirectionType = RouterDirection.Right;
    const UP: RouterDirectionType = RouterDirection.Up;
    const DOWN: RouterDirectionType = RouterDirection.Down;

    const xDiff: number = fromPt.x - toPt.x;
    const yDiff: number = fromPt.y - toPt.y;
    let point: Point;
    let pos: number;
    let dir: RouterDirectionType;
    if (xDiff * xDiff < TOLxTOL && yDiff * yDiff < TOLxTOL) {
      conn.push(new Point(toPt.x, toPt.y));
      return;
    }
    if (fromDir == LEFT) {
      if (xDiff > 0 && yDiff * yDiff < TOL && toDir == RIGHT) {
        point = toPt;
        dir = toDir;
      } else {
        if (xDiff < 0) {
          point = new Point(fromPt.x - MINDIST, fromPt.y);
        } else if ((yDiff > 0 && toDir == DOWN) || (yDiff < 0 && toDir == UP)) {
          point = new Point(toPt.x, fromPt.y);
        } else if (fromDir == toDir) {
          pos = Math.min(fromPt.x, toPt.x) - MINDIST;
          point = new Point(pos, fromPt.y);
        } else {
          point = new Point(fromPt.x - xDiff / 2, fromPt.y);
        }
        if (yDiff > 0) {
          dir = UP;
        } else {
          dir = DOWN;
        }
      }
    } else if (fromDir == RIGHT) {
      if (xDiff < 0 && yDiff * yDiff < TOL && toDir == LEFT) {
        point = toPt;
        dir = toDir;
      } else {
        if (xDiff > 0) {
          point = new Point(fromPt.x + MINDIST, fromPt.y);
        } else if ((yDiff > 0 && toDir == DOWN) || (yDiff < 0 && toDir == UP)) {
          point = new Point(toPt.x, fromPt.y);
        } else if (fromDir == toDir) {
          pos = Math.max(fromPt.x, toPt.x) + MINDIST;
          point = new Point(pos, fromPt.y);
        } else {
          point = new Point(fromPt.x - xDiff / 2, fromPt.y);
        }
        if (yDiff > 0) {
          dir = UP;
        } else {
          dir = DOWN;
        }
      }
    } else if (fromDir == DOWN) {
      if (xDiff * xDiff < TOL && yDiff < 0 && toDir == UP) {
        point = toPt;
        dir = toDir;
      } else {
        if (yDiff > 0) {
          point = new Point(fromPt.x, fromPt.y + MINDIST);
        } else if ((xDiff > 0 && toDir == RIGHT) || (xDiff < 0 && toDir == LEFT)) {
          point = new Point(fromPt.x, toPt.y);
        } else if (fromDir == toDir) {
          pos = Math.max(fromPt.y, toPt.y) + MINDIST;
          point = new Point(fromPt.x, pos);
        } else {
          point = new Point(fromPt.x, fromPt.y - yDiff / 2);
        }
        if (xDiff > 0) {
          dir = LEFT;
        } else {
          dir = RIGHT;
        }
      }
    } else {
      if (xDiff * xDiff < TOL && yDiff > 0 && toDir == DOWN) {
        point = toPt;
        dir = toDir;
      } else {
        if (yDiff < 0) {
          point = new Point(fromPt.x, fromPt.y - MINDIST);
        } else if ((xDiff > 0 && toDir == RIGHT) || (xDiff < 0 && toDir == LEFT)) {
          point = new Point(fromPt.x, toPt.y);
        } else if (fromDir == toDir) {
          pos = Math.min(fromPt.y, toPt.y) - MINDIST;
          point = new Point(fromPt.x, pos);
        } else {
          point = new Point(fromPt.x, fromPt.y - yDiff / 2);
        }
        if (xDiff > 0) {
          dir = LEFT;
        } else {
          dir = RIGHT;
        }
      }
    }
    this.route(conn, point, dir, toPt, toDir);
    conn.push(fromPt);
  }
}
